﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Serialization;
using BOL.Apps.GeneNetworkBrowser.Databases;
using BOL.Models.Genome;

namespace BOL.Apps.GeneNetworkBrowser
{
    /// <summary>
    /// Represents a gene.
    /// </summary>
    [Serializable]
    public class Gene : ICloneable, IEquatable<Gene>, INotifyPropertyChanged//, ISelectable
    {
        #region Private members

        private int _id;
        private string _symbol;
        private Chromosome _chromosome;
        private CytoBand _cytoBand;
        private string _fullName;
        private Models.Features.GeneType _type;
        private IEnumerable<string> _alternatives;
        private TissueTypes _tissueTypes;
        
        private Dictionary<string, dynamic> _attributes;

        #endregion

        #region Public properties

        /// <summary>Returns the ENTREZ gene ID.</summary>
        [Category("Information")]
        [DisplayName("ENTREZ Gene ID")]
        [Description("Returns the ENTREZ Gene ID of the gene.")]
        [XmlIgnore]
        public int Id { get { return _id; } set { _id = value; } }

        /// <summary>Returns the gene symbol.</summary>
        [Category("Information")]
        [DisplayName("Gene symbol")]
        [Description("Returns the symbol of the gene.")]
        [XmlAttribute]
        public string Symbol { get { return _symbol; } set { _symbol = value; } }

        /// <summary>Returns the chromosome.</summary>
        [Category("Information")]
        [DisplayName("Chromosome")]
        [Description("Returns the chromosome of the gene.")]
        //[Editor(typeof(ChromosomeEditor), typeof(ChromosomeEditor))]
        [XmlIgnore]
        public Chromosome Chromosome { get { return _chromosome; } set { _chromosome = value; } }

        /// <summary>Returns the cytoband.</summary>
        [Category("Information")]
        [DisplayName("CytoBand")]
        [Description("Returns the cytoband of the gene.")]
        //[Editor(typeof(CytoBandEditor), typeof(CytoBandEditor))]
        [XmlIgnore]
        public CytoBand CytoBand { get { return _cytoBand; } set { _cytoBand = value; } }

        /// <summary>Returns the gene full name.</summary>
        [Category("Information")]
        [DisplayName("Full name")]
        [Description("Returns the full name of the gene.")]
        [XmlAttribute]
        public string FullName { get { return _fullName; } set { _fullName = value; } }

        /// <summary>Returns the gene type.</summary>
        [Category("Information")]
        [DisplayName("Type")]
        [Description("Returns the type of the gene.")]
        [XmlIgnore]
        public Models.Features.GeneType Type { get { return _type; } set { _type = value; } }

        /// <summary>Returns the alternative names.</summary>
        [Category("Information")]
        [DisplayName("Alternatives")]
        [Description("Returns the alternatives of the gene.")]
        [XmlIgnore]
        public IEnumerable<string> Alternatives { get { return _alternatives; } set { _alternatives = value; } }

        /// <summary>Returns the tissue type.</summary>
        [Category("Information")]
        [DisplayName("Tissue types")]
        [Description("Returns the tissue types of the gene.")]
        [XmlIgnore]
        public TissueTypes TissueTypes { get { return _tissueTypes; } set { _tissueTypes = value; } }

        /// <summary>Returns the properties of gene.</summary>
        [Browsable(false)]
        [Category("Information")]
        [DisplayName("Attributes")]
        [XmlIgnore]
        public Dictionary<string, dynamic> Attributes
        { 
            get { return _attributes; } 
            set 
            {
                if (value != _attributes)
                {
                    _attributes = value;
                    OnPropertyChanged("Attributes");
                }
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the gene class.
        /// </summary>
        public Gene(int id, string symbol, Chromosome chromosome, CytoBand cytoBand, string fullName, Models.Features.GeneType type, IEnumerable<string> alternatives, TissueTypes tissueTypes)
        {
            _id = id;
            _symbol = symbol;
            _cytoBand = cytoBand;
            _chromosome = chromosome;
            _fullName = fullName;
            _type = type;
            _alternatives = alternatives;
            _tissueTypes = tissueTypes;

            //_isSelected = false;
            _isVisited = false;
            _attributes = new Dictionary<string, dynamic>();
        }

        public Gene(int id) 
            : this(id, String.Empty, null, null, String.Empty, Models.Features.GeneType.Unknown, null, TissueTypes.None) { }

        #endregion

        #region IVisit implementation

        private bool _isVisited;

        [Browsable(false)]
        public bool IsVisited { get { return _isVisited; } }

        public void Visit()
        {
            _isVisited = true;
        }

        public void NeverVisit()
        {
            _isVisited = false;
        }

        #endregion

        //#region IVisible implementation

        //private bool _isVisible;

        //[Browsable(false)]
        //public bool IsVisible { get { return _isVisible; } set { _isVisible = value; } }

        //public void Show()
        //{
        //    _isVisible = true;
        //}

        //public void Hide()
        //{
        //    _isVisible = false;
        //}

        //#endregion

        //#region ISelected implementation

        //private bool _isSelected;

        //[Browsable(false)]
        //public bool IsSelected { get { return _isSelected; } set { _isSelected = value; } }

        //public void Select()
        //{
        //    _isSelected = true;
        //}

        //public void Deselect()
        //{
        //    _isSelected = false;
        //}

        //#endregion

        #region ICloneable implementation

        /// <summary>
        /// Creates an exact copy of current gene.
        /// </summary>
        /// <returns>Gene</returns>
        public Gene Clone()
        {
            return new Gene(_id, _symbol, _chromosome, _cytoBand, _fullName, _type, _alternatives, _tissueTypes);
        }

        /// <summary>
        /// Creates an exact copy of current gene as an object. 
        /// </summary>
        /// <returns>Gene</returns>
        object ICloneable.Clone()
        {
            return Clone();
        }

        #endregion

        #region IEquatable<Gene> implementation

        public bool Equals(Gene other)
        {
            return _id.Equals(other._id);
        }

        #endregion

        #region ISelectable implementation

        protected bool _isSelected;

        public bool IsSelected { get { return _isSelected; } }

        public void Select()
        {
            _isSelected = true;
            OnPropertyChanged("IsSelected");
        }

        public void Deselect()
        {
            _isSelected = false;
            OnPropertyChanged("IsSelected");
        }

        #endregion

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, e);
        }

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        protected void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                throw new ArgumentNullException("PropertyNames cannot be null.");

            foreach (var propertyName in propertyNames)
                OnPropertyChanged(propertyName);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return _id;
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return base.Equals(obj);

            if (!(obj is Gene))
                throw new InvalidCastException("The 'obj' argument is not a Gene object.");
            else
                return Equals(obj as Gene);
        }

        public override string ToString()
        {
            return (String.IsNullOrEmpty(_symbol)) ? _id.ToString() : _symbol;
        }

        #endregion
    }
}
